package com.ybw.order.demo.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ybw.order.demo.constant.OrderConstant;
import com.ybw.order.demo.constant.RedisPreConstant;
import com.ybw.order.demo.constant.ReturnCode;
import com.ybw.order.demo.constant.db.GoodsInfoDbConstant;
import com.ybw.order.demo.dto.HandleStockStatusDTO;
import com.ybw.order.demo.entity.GoodsInfo;
import com.ybw.order.demo.exception.BizException;
import com.ybw.order.demo.mapper.GoodsInfoMapper;
import com.ybw.order.demo.service.GoodsInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ybw.order.demo.service.RedisService;
import com.ybw.order.demo.utils.GsonUtils;
import com.ybw.order.demo.vo.base.req.PageBaseReqVO;
import com.ybw.order.demo.vo.base.res.PageBaseResVO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 商品 服务实现类
 * </p>
 *
 * @author ybwei
 * @since 2022-02-20
 */
@Service
public class GoodsInfoServiceImpl extends ServiceImpl<GoodsInfoMapper, GoodsInfo> implements GoodsInfoService {

    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private GoodsInfoMapper goodsInfoMapper;
    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    private RedisService redisService;

    @Override
    public void todaySellGoodsInfoListTask() {
        //1、获取今天开始、结束时间
        LocalDateTime todayStart = LocalDateTime.now().with(LocalTime.MIN);
        LocalDateTime todayEnd = LocalDateTime.now().with(LocalTime.MAX);
        //2、获取今天开售的商品
        List<GoodsInfo> goodsInfoList = this.lambdaQuery().le(GoodsInfo::getStartSaleTime, todayEnd).ge(GoodsInfo::getEndSaleTime, todayStart).eq(GoodsInfo::getStatus, GoodsInfoDbConstant.Status.WAIT_SALE.getCode()).list();
        if (CollectionUtils.isEmpty(goodsInfoList)) {
            return;
        }
        //3、存入Redis
        setTodaySellGoodsInfoInRedis(goodsInfoList);
    }

    /**
     * 今天售卖的商品存入缓存
     *
     * @param goodsInfoList
     * @methodName: setTodaySellGoodsInfoInRedis
     * @return: void
     * @author: ybwei
     * @date: 2022/9/4
     **/
    private void setTodaySellGoodsInfoInRedis(List<GoodsInfo> goodsInfoList) {
        String todaySellGoodsInfoListKey = getTodaySellGoodsInfoListKey();
        redisTemplate.opsForValue().set(todaySellGoodsInfoListKey, goodsInfoList, 1, TimeUnit.DAYS);
    }

    /**
     * 今天售卖的商品存入缓存-异步
     *
     * @param goodsInfoList
     * @methodName: asynSetTodaySellGoodsInfoInRedis
     * @return: void
     * @author: ybwei
     * @date: 2022/9/4
     **/
    private void asynSetTodaySellGoodsInfoInRedis(List<GoodsInfo> goodsInfoList) {
        threadPoolTaskExecutor.execute(() -> {
            setTodaySellGoodsInfoInRedis(goodsInfoList);
        });
    }

    /**
     * @methodName: getTodaySellGoodsInfoListKey
     * @return: java.lang.String
     * @author: ybwei
     * @date: 2022/9/1
     **/
    private String getTodaySellGoodsInfoListKey() {
        return new StringBuilder().append(RedisPreConstant.TODAY_SELL_GOODS_INFO_LIST).append(LocalDate.now()).toString();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PageBaseResVO list(PageBaseReqVO pageBaseReqVO) {
        //1、更新状态：待发售-发售中
        updateStatus();
        //2、获取列表
        IPage<GoodsInfo> page = new Page<>(pageBaseReqVO.getPageNum(), pageBaseReqVO.getPageSize());
        this.lambdaQuery().orderByAsc(GoodsInfo::getStatus).page(page);
        return new PageBaseResVO(page.getTotal(), page.getRecords());
    }

    @Override
    public GoodsInfo detail(Long goodsInfoId) {
        return getGoodsInfo(goodsInfoId);
    }

    /**
     * @param goodsInfoId
     * @methodName: getGoodsInfo
     * @return: com.ybw.order.demo.entity.GoodsInfo
     * @author: ybwei
     * @date: 2022/9/1
     **/
    @Override
    public GoodsInfo getGoodsInfo(Long goodsInfoId) {
        //1、从Redis中获取商品信息
        String goodsInfoKey = getGoodsInfoKey(goodsInfoId);
        GoodsInfo goodsInfo = redisService.hmget(goodsInfoKey,GoodsInfo.class);
        if (goodsInfo != null) {
            return goodsInfo;
        }
        //2、从数据库中获取
        goodsInfo = this.getById(goodsInfoId);
        if (goodsInfo == null) {
            return null;
        }
        //3、商品信息存入Redis,有效期1天
        redisService.hmSetObject(goodsInfoKey, goodsInfo, 1L, TimeUnit.DAYS);
        return goodsInfo;
    }

    /**
     * 更新状态：待发售-发售中
     *
     * @methodName: updateStatus
     * @return: void
     * @author: ybwei
     * @date: 2022/9/1
     **/
    private void updateStatus() {
        //1、从缓存中获取今天待售的商品
        String todaySellGoodsInfoListKey = getTodaySellGoodsInfoListKey();
        List<GoodsInfo> goodsInfoList = (List<GoodsInfo>) redisTemplate.opsForValue().get(todaySellGoodsInfoListKey);
        if (CollectionUtils.isEmpty(goodsInfoList)) {
            return;
        }
        //2、获取待修改的商品:过滤发售时间小于当前时间且"待发售"的商品
        List<GoodsInfo> updateGoodsInfoList = goodsInfoList.stream().filter(goodsInfo -> goodsInfo.getStartSaleTime().compareTo(LocalDateTime.now()) <= 0 && GoodsInfoDbConstant.Status.WAIT_SALE.getCode().equals(goodsInfo.getStatus())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(updateGoodsInfoList)) {
            return;
        }
        //3、更新状态：待发售-发售中
        updateGoodsInfoList.forEach(goodsInfo -> {
            //3.1 更新状态=发售中
            this.lambdaUpdate()
                    .eq(GoodsInfo::getStatus, GoodsInfoDbConstant.Status.WAIT_SALE.getCode())
                    .eq(GoodsInfo::getId, goodsInfo.getId())
                    .set(GoodsInfo::getStatus, GoodsInfoDbConstant.Status.ON_SALE.getCode())
                    .update();
        });
        //4、异步重新存入Redis
        //4.1 删除更新过的商品
        goodsInfoList.removeIf(goodsInfo -> updateGoodsInfoList.stream().anyMatch(updateGoodsInfo -> updateGoodsInfo.getId().equals(goodsInfo.getId())));
        //4.2 异步重新存入Redis
        asynSetTodaySellGoodsInfoInRedis(goodsInfoList);
    }

    @Override
    public void handleStockStatusInRedis(Long goodsId, HandleStockStatusDTO.Handle handle) {
        //1、获取goodsInfoKey
        String goodsInfoKey = getGoodsInfoKey(goodsId);
        switch (handle) {
            case MINUS:
                //2、减库存
                long stockCount = redisTemplate.opsForHash().increment(goodsInfoKey, OrderConstant.Create.GOODS_INFO_DTO_STOCK_COUNT, -OrderConstant.Create.BUY_COUNT);
                if (stockCount < 0) {
                    //2.1 库存需要加回去
                    redisTemplate.opsForHash().increment(goodsInfoKey, OrderConstant.Create.GOODS_INFO_DTO_STOCK_COUNT, OrderConstant.Create.BUY_COUNT);
                    //2.2 超卖情况，抛出异常
                    throw new BizException(ReturnCode.GOODS_INFO_NOT_ENOUGH);
                }
                break;
            case PLUS:
                //2、加库存
                stockCount = redisTemplate.opsForHash().increment(goodsInfoKey, OrderConstant.Create.GOODS_INFO_DTO_STOCK_COUNT, OrderConstant.Create.BUY_COUNT);
                break;
            default:
                break;
        }
    }

    @Override
    public void handleStockStatusInMysql(HandleStockStatusDTO handleStockStatusDTO) {
        //1、查询商品信息
        GoodsInfo goodsInfo = this.getById(handleStockStatusDTO.getGoodsId());
        if (goodsInfo == null) {
            return;
        }
        //2、减库存并判断商品状态
        switch (handleStockStatusDTO.getHandle()) {
            case MINUS:
                //2.1 减库存
                minusStockStatus(goodsInfo);
                break;
            case PLUS:
                //2.1 加库存
                plusStockStatus(goodsInfo);
                break;
            default:
                break;
        }
    }

    /**
     * 减库存并判断商品状态（mysql）
     *
     * @param goodsInfo
     * @methodName: minusStockStatus
     * @return: void
     * @author: ybwei
     * @date: 2022/9/1
     **/
    private void minusStockStatus(GoodsInfo goodsInfo) {
        if (goodsInfo.getStockCount() - OrderConstant.Create.BUY_COUNT <= 0) {
            goodsInfoMapper.minusStockStatus(goodsInfo.getId(), OrderConstant.Create.BUY_COUNT);
        } else {
            goodsInfoMapper.minusStock(goodsInfo.getId(), OrderConstant.Create.BUY_COUNT);
        }
    }

    /**
     * 加库存并判断商品状态（mysql）
     *
     * @param goodsInfo
     * @methodName: plusStockStatus
     * @return: void
     * @author: ybwei
     * @date: 2022/9/1
     **/
    private void plusStockStatus(GoodsInfo goodsInfo) {
        if (goodsInfo.getEndSaleTime() != null && goodsInfo.getEndSaleTime().isBefore(LocalDateTime.now())) {
            //加库存时如果活动已结束更改为已结束状态
            goodsInfoMapper.plusStockStatusEnd(goodsInfo.getId(), OrderConstant.Create.BUY_COUNT);
        } else {
            if (goodsInfo.getStockCount() == 0) {
                //库存=0
                goodsInfoMapper.plusStockStatus(goodsInfo.getId(), OrderConstant.Create.BUY_COUNT);
            } else {
                goodsInfoMapper.plusStock(goodsInfo.getId(), OrderConstant.Create.BUY_COUNT);
            }
        }
    }

    private String getGoodsInfoKey(Long goodsInfoId) {
        return new StringBuilder(RedisPreConstant.GOODS_INFO).append(goodsInfoId).toString();
    }
}
